package cre.tomcat7.ch08.core;

import org.apache.catalina.*;
import org.apache.catalina.util.LifecycleSupport;

import javax.naming.directory.DirContext;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import java.beans.PropertyChangeListener;
import java.io.IOException;

/**
 * @Author penglai
 * @Date 2017/1/27.
 */
public class SimpleWrapper implements Wrapper, Pipeline, Lifecycle {

    private Loader loader;
    private Container parent;
    private Pipeline pipeline = new SimplePipeline(this);
    private Servlet instance;
    private String servletClass;
    private String name;

    //for lifecycle
    private boolean started;
    private LifecycleSupport lifecycleSupport = new LifecycleSupport(this);

    public SimpleWrapper() {
        pipeline.setBasic(new SimpleWrapperValve());
    }

    public void addValve(Valve valve) {
        pipeline.addValve(valve);
    }

    public Valve getBasic() {
        return pipeline.getBasic();
    }

    public Valve[] getValves() {
        return pipeline.getValves();
    }

    public void addInitParameter(String s, String s1) {

    }

    public void addInstanceListener(InstanceListener instanceListener) {

    }

    public void addSecurityReference(String s, String s1) {

    }

    public Servlet allocate() throws ServletException {
        if (instance == null) {
            instance = loadServlet();
        }
        return instance;
    }

    private Servlet loadServlet() throws ServletException {
        if (instance != null) {
            return instance;
        }
        String actualClass = getServletClass();
        if (actualClass == null) {
            throw new ServletException("servlet class has not been specified");
        }

        Loader loader = getLoader();
        if (loader == null) {
            throw new ServletException("No loader.");
        }

        Class servletClazz = null;
        ClassLoader classLoader = loader.getClassLoader();
        try {
            if (classLoader != null) {
                servletClazz = classLoader.loadClass(actualClass);
            }
        } catch (ClassNotFoundException e) {
            throw new ServletException("servlet class not found " + actualClass);
        }

        Servlet servlet = null;
        try {
            servlet = (Servlet) servletClazz.newInstance();
        } catch (Throwable e) {
            throw new ServletException("Failed to instantiate servlet");
        }
        try {
            servlet.init(null);
        } catch (Throwable e) {
            throw new ServletException("Failed to initialize servlet");
        }

        return servlet;
    }

    public void deallocate(Servlet servlet) throws ServletException {

    }

    public String findInitParameter(String s) {
        return null;
    }

    public String[] findInitParameters() {
        return new String[0];
    }

    public String findSecurityReference(String s) {
        return null;
    }

    public String[] findSecurityReferences() {
        return new String[0];
    }

    public long getAvailable() {
        return 0;
    }

    public String getJspFile() {
        return null;
    }

    public int getLoadOnStartup() {
        return 0;
    }

    public String getRunAs() {
        return null;
    }

    public String getServletClass() {
        return servletClass;
    }

    public boolean isUnavailable() {
        return false;
    }

    public void load() throws ServletException {
        instance = loadServlet();
    }

    public void removeInitParameter(String s) {

    }

    public void removeInstanceListener(InstanceListener instanceListener) {

    }

    public void removeSecurityReference(String s) {

    }

    public void setAvailable(long l) {

    }

    public void setJspFile(String s) {

    }

    public void setLoadOnStartup(int i) {

    }

    public void setRunAs(String s) {

    }

    public void setServletClass(String s) {
        this.servletClass = s;
    }

    public void unavailable(UnavailableException e) {

    }

    public void unload() throws ServletException {

    }

    public void addChild(Container container) {

    }

    public void addContainerListener(ContainerListener containerListener) {

    }

    public void addMapper(Mapper mapper) {

    }

    public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) {

    }

    public Container findChild(String s) {
        return null;
    }

    public Container[] findChildren() {
        return new Container[0];
    }

    public ContainerListener[] findContainerListeners() {
        return new ContainerListener[0];
    }

    public Mapper findMapper(String s) {
        return null;
    }

    public Mapper[] findMappers() {
        return new Mapper[0];
    }

    public Cluster getCluster() {
        return null;
    }

    public String getInfo() {
        return null;
    }

    public Loader getLoader() {
        if (loader != null) {
            return loader;
        }
        if (parent != null) {
            return parent.getLoader();
        }
        return null;
    }

    public Logger getLogger() {
        return null;
    }

    public Manager getManager() {
        return null;
    }

    public String getName() {
        return name;
    }

    public Container getParent() {
        return parent;
    }

    public ClassLoader getParentClassLoader() {
        return null;
    }

    public Realm getRealm() {
        return null;
    }

    public DirContext getResources() {
        return null;
    }

    public void invoke(Request request, Response response) throws IOException, ServletException {
        pipeline.invoke(request, response);
    }

    public void removeValve(Valve valve) {
        pipeline.removeValve(valve);
    }

    public void setBasic(Valve valve) {
        pipeline.setBasic(valve);
    }

    public Container map(Request request, boolean b) {
        return null;
    }

    public void removeChild(Container container) {

    }

    public void removeContainerListener(ContainerListener containerListener) {

    }

    public void removeMapper(Mapper mapper) {

    }

    public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener) {

    }

    public void setCluster(Cluster cluster) {

    }

    public void setLoader(Loader loader) {
        this.loader = loader;
    }

    public void setLogger(Logger logger) {

    }

    public void setManager(Manager manager) {

    }

    public void setName(String s) {
        this.name = s;
    }

    public void setParent(Container container) {
        this.parent = container;
    }

    public void setParentClassLoader(ClassLoader classLoader) {

    }

    public void setRealm(Realm realm) {

    }

    public void setResources(DirContext dirContext) {

    }

    public void addLifecycleListener(LifecycleListener listener) {

    }

    public LifecycleListener[] findLifecycleListeners() {
        return new LifecycleListener[0];
    }

    public void removeLifecycleListener(LifecycleListener listener) {

    }

    public synchronized void start() throws LifecycleException {
        if (started) {
            throw new LifecycleException("SimpleWrapper has already started");
        }
        started = true;

        try {
            if (loader instanceof Lifecycle) {
                ((Lifecycle) loader).start();
            }

            if (pipeline instanceof Lifecycle) {
                ((Lifecycle) pipeline).start();
            }
            lifecycleSupport.fireLifecycleEvent(START_EVENT, this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public synchronized void stop() throws LifecycleException {
        if (!started) {
            throw new LifecycleException("SimpleWrapper has not been started");
        }

        started = false;
        try {
            instance.destroy();
            instance = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            if (pipeline instanceof Lifecycle) {
                ((Lifecycle) pipeline).stop();
            }

            if (loader instanceof Lifecycle) {
                ((Lifecycle) loader).stop();
            }
            lifecycleSupport.fireLifecycleEvent(STOP_EVENT, this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
